﻿#region copyright
//                Copyright Andrew Rafas 2012.
// Distributed under the Eclipse Public License, Version 1.0.
//           (See accompanying file LICENSE.txt or 
//     copy at http://www.eclipse.org/legal/epl-v10.html)
#endregion
using System;
using System.Diagnostics.Contracts;
using System.Collections.Generic;

namespace CSFP
{
    [ContractClass(typeof(ISeqContract<,>))]
    public interface ISeq<T, S>
        where S : struct, ISeq<T, S>
    {
        // We cannot work with a HasNext since we cannot check whether this struct is null or not 
        // like it was done in Clojure (so we have to have a way to decide if there is a First).
        bool IsValid { get; }
        T First { get; }
        S Next { get; }
    }

    public static partial class Functions
    {
        public static void ForEach<T, S>(this S seq, Action<T> action)
            where S : struct, ISeq<T, S>
        {
            Contract.Requires<ArgumentNullException>(action != null);

            while (seq.IsValid) {
                action(seq.First);
                seq = seq.Next;
            }
        }

        public static bool ValueEquals<T, S1, S2>(S1 seq1, S2 seq2)
            where S1 : struct, ISeq<T, S1>
            where S2 : struct, ISeq<T, S2>
            where T : IEquatable<T>
        {
            while (seq1.IsValid && seq2.IsValid) {
                if (!seq1.First.Equals(seq2.First))
                    return false;
                seq1 = seq1.Next;
                seq2 = seq2.Next;
            }
            return (seq1.IsValid == seq2.IsValid);
        }

        // boxes seq2!!!
        public static bool ValueEquals<T, S1, S2>(this S1 seq1, ISeq<T, S2> seq2)
            where S1 : struct, ISeq<T, S1>
            where S2 : struct, ISeq<T, S2>
            where T : IEquatable<T>
        {
            return ValueEquals<T, S1, S2>(seq1, (S2)seq2);
        }
    }
}
